package cn.kai.process.flow.core;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.kai.enums.FlowNodeTypeEnums;
import cn.kai.json.flow.FlowChainJson;
import cn.kai.json.flow.FlowJson;
import cn.kai.json.flow.FlowNodeJson;
import cn.kai.process.cache.LFUCache;
import cn.kai.process.chain.CFRunChain;
import cn.kai.process.flow.node.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 流程引擎
 Author: cKnight
 Date: 2025/1/23
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class FlowEngine {
    private final LFUCache<String,FlowChain> chains = new LFUCache<>(200);

    private final ThreadPoolTaskExecutor threadPoolTaskExecutor;

    /** 注册一个流程链 */
    public void registerChain(String name,FlowChain chain){
        chains.put(name,chain);
    }

    /** 执行一个流程链 */
    public void execute(String name){
        execute(name,new NodeContext());
    }

    /** 执行一个流程链 */
    public void execute(String name,NodeContext nodeContext){
        FlowChain flowChain = chains.get(name);
        if (Objects.isNull(flowChain)){
            throw new IllegalArgumentException("流程链未找到:"+name);
        }
        flowChain.execute(nodeContext);
    }

    /** 注册并启动 */
    public void execute(FlowChainJson chainJson){
        FlowChain flowChain = parseFlowChain(chainJson);
        registerChain(flowChain.getName(),flowChain);
        execute(flowChain.getName());
    }

    /** 更新一个流程链 */
    public void updateChain(String name ,FlowChain chain){
        chains.put(name,chain);
    }

    /** 获取所有的流程链 */
    public Map<String,FlowChain> getAllChain(){
        return chains.getCache();
    }

    /** 解析并注册 */
    public void registerChain(FlowJson flow){
        for (FlowChainJson chain : flow.getChains()) {
            if (!chain.getIsLazy()){
                FlowChain flowChain = parseFlowChain(chain);
                if (Objects.nonNull(flowChain)){
                    registerChain(flowChain.getName(),flowChain);
                }
            }
        }
    }

    /** 解析单个 */
    public void registerChain(FlowChainJson chainJson){
        FlowChain flowChain = parseFlowChain(chainJson);
        registerChain(flowChain.getName(),flowChain);
    }

    /** 解析一个chain */
    public FlowChain parseFlowChain(FlowChainJson chainJson){
        String name = chainJson.getName();
        if (StringUtils.isBlank(name)){
            log.warn("解析chain失败，未发现chain名称");
            return null;
        }
        FlowChain flowChain = new FlowChain(name);
        List<Node> nodes = parseNodeList(chainJson.getNodes());
        flowChain.setNodes(nodes);
        return flowChain;
    }

    private List<Node> parseNodeList(List<FlowNodeJson> nodes){
        List<Node> list = new ArrayList<>();
        for (FlowNodeJson nodeJson : nodes) {
            Node node = parseNode(nodeJson);
            if (Objects.nonNull(node)){
                list.add(node);
            }else {
                log.warn("节点解析失败:{}",nodeJson);
            }
        }
        return list;
    }

    private List<Node> parseNodeList(List<FlowNodeJson> nodes,List<Node> list){
        for (FlowNodeJson nodeJson : nodes) {
            Node node = parseNode(nodeJson);
            if (Objects.nonNull(node)){
                list.add(node);
            }else {
                log.warn("节点解析失败:{}",nodeJson);
            }
        }
        return list;
    }

    private Node parseNode(FlowNodeJson node){
        FlowNodeTypeEnums type = node.getType();
        if (Objects.isNull(type)){
            throw new IllegalArgumentException("节点类型为空:"+ node);
        }
        String component = node.getComponent();
        Node bean = null;
        if (StringUtils.isNotBlank(component)){
            bean = SpringUtil.getBean(component, Node.class);
        }
        switch (type){
            case NODE:
                checkNode(component,bean);
                return bean;
            case IF:
                String condition = node.getCondition();
                List<FlowNodeJson> trueChain = node.getTrueChain();
                List<FlowNodeJson> falseChain = node.getFalseChain();
                if (StringUtils.isBlank(condition)){
                    throw new IllegalArgumentException("未获取到节点条件key:"+node);
                }
                if (CollUtil.isEmpty(trueChain) || CollUtil.isEmpty(falseChain)){
                    throw new IllegalArgumentException("未获取到节点条件true或false链:"+node);
                }
                //并行解析双分支
                List<Node> trueNodes = new ArrayList<>();
                List<Node> falseNodes = new ArrayList<>();
                CFRunChain.start(threadPoolTaskExecutor)
                        .next(()-> parseNodeList(trueChain,trueNodes))
                        .next(()-> parseNodeList(falseChain,falseNodes))
                        .end(60L);
                return new ConditionNode(condition,new ChainNode(trueNodes),new ChainNode(falseNodes));
            case LOOP:
                checkNode(component,bean);
                return new LoopNode(bean,node.getTimes());
            case ASYNC:
                checkNode(component,bean);
                return new AsyncNode(bean);
            case RETRY:
                checkNode(component,bean);
                return new RetryNode(bean,node.getTimes());
            case PARALLEL:
                List<FlowNodeJson> parallelList = node.getTrueChain();
                if (CollUtil.isEmpty(parallelList)){
                    throw new IllegalArgumentException("并行节点获取到nodes:"+node);
                }
                List<Node> nodes = parseNodeList(parallelList, new ArrayList<>());
                return new ParallelNode(nodes,node.getTimes());
            default:
                throw new IllegalArgumentException("node类型错误:"+node);
        }
    }

    private void checkNode(String component,Node node){
        if (StringUtils.isBlank(component) || Objects.isNull(node)){
            throw new IllegalArgumentException("未获取到节点组件");
        }
    }
}
